//
// modeldata 
// parse in text model data text file.
// tfranguiadakis 2009
//
#include <vector>
#include <string>

#include "modeldata.h"

using namespace ModelData;
using namespace std;

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

// -----------------------------------------------------------------------------
// parsing helpers
// -----------------------------------------------------------------------------
void setTextureAtlasParams(TextureAtlas& atlas, string& i_args)
{
    vector<string> items;
    items = split(i_args, ' ', items);

    NSArray* items = [i_args componentsSeparatedByCharactersInSet:charSet];
    
    int cnt = 0;
    int argcnt = 0;
    while (cnt < [items count])
    {
        if ([[items objectAtIndex:cnt] compare:@""] != NSOrderedSame)
        {
            if (argcnt == 0){
                atlas.name = [[items objectAtIndex:cnt] UTF8String];
                argcnt++;
            }
            if (argcnt == 1){
                atlas.xs = [[items objectAtIndex:cnt] intValue];
            }
            else if (argcnt == 2)
            {
                atlas.ys = [[items objectAtIndex:cnt] intValue];
                return; // nothing more
            }
        }
        ++cnt;        
    }
}

void setSequenceParams(Sequence& seq, NSString* args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@"\" "];
    
    NSArray* items = [args componentsSeparatedByCharactersInSet:charSet];
    
    int cnt = 0;
    int argcnt = 0;
    while (cnt < [items count])
    {
        if ([[items objectAtIndex:cnt] compare:@""] != NSOrderedSame)
        {
            if (argcnt == 0){
                seq.name = [[items objectAtIndex:cnt] UTF8String];
                argcnt++;
            }
            else {
                seq.loop = [[items objectAtIndex:cnt] intValue];
                return; // nothing more
            }
        }
        ++cnt;        
    }
}

void setName(std::string& name, NSString* args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@"\" "];
    
    NSArray* items = [args componentsSeparatedByCharactersInSet:charSet];
    
    int cnt = 0;
    int argcnt = 0;
    while (cnt < [items count])
    {
        if ([[items objectAtIndex:cnt] compare:@""] != NSOrderedSame)
        {
            if (argcnt == 0){
                name = [[items objectAtIndex:cnt] UTF8String];
                argcnt++;
            }
        }
        ++cnt;        
    }
}

void setVec2(float vec[2], NSString* args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@" "];
    
    NSArray* items = [args componentsSeparatedByCharactersInSet:charSet];
    
    int cnt = 0;
    int argcnt = 0;
    while (cnt < [items count])
    {
        if ([[items objectAtIndex:cnt] compare:@""] != NSOrderedSame)
        {
            if (argcnt < 2){
                vec[argcnt] = [[items objectAtIndex:cnt] floatValue];
                argcnt++;
            }
        }
        ++cnt;        
    }
}

void setFloat(float& o_f32, NSString* i_args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@" "];
    
    NSArray* items = [i_args componentsSeparatedByCharactersInSet:charSet];
    
    int cnt = 0;
    int argcnt = 0;
    while (cnt < [items count])
    {
        if ([[items objectAtIndex:cnt] compare:@""] != NSOrderedSame)
        {
            if (argcnt < 1){
                o_f32 = [[items objectAtIndex:cnt] floatValue];
                argcnt++;
            }
        }
        ++cnt;        
    }
}

void toFloatVector(std::vector<float>& floatvec, NSString* args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@" "];
    
    NSArray* items = [args componentsSeparatedByCharactersInSet:charSet];
    
    for (NSString* item in items)
    {
        if ([item compare:@""] != NSOrderedSame)
            floatvec.push_back( [item floatValue] );
    }
}

void toAnimType(Anim::AnimType& o_type, NSString* args)
{
    NSCharacterSet* charSet = 
    [NSCharacterSet characterSetWithCharactersInString:@" "];
    
    NSArray* items = [args componentsSeparatedByCharactersInSet:charSet];
    
    for (NSString* item in items)
    {
        if ([item caseInsensitiveCompare:@"pos"]  == NSOrderedSame)
        {   
            o_type = Anim::POS;
            return;
        }
        if ([item caseInsensitiveCompare:@"rot"]  == NSOrderedSame)
        {
            o_type = Anim::ROT;
            return;
        }
        if ([item caseInsensitiveCompare:@"scale"] == NSOrderedSame)
        {
            o_type = Anim::SCALE;
            return;
        }
        if ([item caseInsensitiveCompare:@"uvpos"] == NSOrderedSame)
        {   
            o_type = Anim::UVPOS;
            return;
        }
        if ([item caseInsensitiveCompare:@"uvrot"] == NSOrderedSame)
        {
            o_type = Anim::UVROT;
            return;
        }
        if ([item caseInsensitiveCompare:@"uvscale"] == NSOrderedSame)
        {
            o_type = Anim::UVSCALE;
            return;
        }
        if ([item caseInsensitiveCompare:@"atlas"] == NSOrderedSame)
        {
            o_type = Anim::ATLAS;
            return;
        }
    }
    // WARN?
    o_type = Anim::UNDEFINED;
    
}

void toGeomType(Geom::GeomType& o_type, NSString* i_args)
{
    NSCharacterSet* charSet = 
        [NSCharacterSet characterSetWithCharactersInString:@" "];
    
    NSArray* items = [i_args componentsSeparatedByCharactersInSet:charSet];
    
    for (NSString* item in items)
    {
        if ([item caseInsensitiveCompare:@"quad"]  == NSOrderedSame)
        {   
            o_type = Geom::QUAD;
            return;
        }
    }

    o_type = Geom::DEFAULT;
    
}


Texture* new_Texture(NSString* i_str)
{
    Texture* texture = new Texture;
    setName(texture->name, i_str);
    texture->type = Texture::PNG;
    return texture;
}

TextureAtlas* new_TextureAtlas(NSString* i_str)
{
    TextureAtlas* textureatlas = new TextureAtlas;
    textureatlas->type = Texture::ATLAS;
    textureatlas->xs   = 1;
    textureatlas->ys   = 1;
    setTextureAtlasParams(*textureatlas, i_str);
    return textureatlas;
}


struct FileRez
{
    std::vector<Anim*> anims;
    std::vector<Model*> models;
    
    Model* addModel() {
        models.push_back(new Model());
        return models.back();
    }
    Anim* addAnim() {
        anims.push_back(new Anim());
        return anims.back();
    }
	
	const Model* findModelRez(std::string& i_modelName)
	{
		for (int i = 0; i < model.size(); i++)
		{
			if (model[i].name == i_modelName)
			{
				return model[i];
			}
		}
		
		return 0;
	}
	
};

FileRez g_modelData;

// -----------------------------------------------------------------------------
// Validate input
#pragma mark Validate_input

const char* depthTabs(int depth)
{
    if (depth <= 0)
        return "";
    const char* tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
    const size_t tabLen(strlen(tabs));
    return tabs + ((tabLen)-depth);
}


const char* animTypeToString(const Anim::AnimType& i_type)
{
    const char* names[] = {
    "POS","SCALE","ROT","UVPOS","UVROT","UVSCALE","ATLAS","UNDEFINED"};
    return names[(int)i_type];
}


const char* geomTypeToString(const Geom::GeomType& i_type)
{
    const char* names[] = {
    "QUAD","DEFAULT"};
    return names[(int)i_type];
}

void printSequences(std::vector<Sequence*>& i_sequences, int depth)
{
    for (int i = 0 ; i < i_sequences.size() ; ++i)
    {
        Sequence& sequence = *i_sequences[i];
        printf("%ssequence: \"%s\" %d\n", depthTabs(depth), sequence.name.c_str(), sequence.loop);
        if (sequence.animNames.size())
        {
            for (int j = 0 ; j < sequence.animNames.size(); ++j)
            {
                printf("%s\tanim: %s\n", depthTabs(depth),sequence.animNames[j].c_str());
            }
        }
        if (sequence.children.size())
        {
            printSequences(sequence.children, depth+1);
        }
        printf("%send:\n", depthTabs(depth));
    }
}

void printModel(Model* model, int depth = 0)
{
    if (depth ==0)
        printf("model: \"%s\"\n", model->name.c_str());
    else
        printf("%schild: \"%s\"\n", depthTabs(depth), model->name.c_str());
    printf("%s\tgeom: %s\n", depthTabs(depth), geomTypeToString(model->geom.type));
    printf("%s\tpos: %.2f %.2f\n", depthTabs(depth), model->pos[0], model->pos[1]);
    printf("%s\trot: %.2f\n", depthTabs(depth), model->rot);
    printf("%s\tscale: %.2f %.2f\n", depthTabs(depth), model->scale[0], model->scale[1]);
    
    if (model->texture)
        if (model->texture->type == Texture::ATLAS)
        {
            TextureAtlas* ta = (TextureAtlas*)model->texture;
            printf("%s\ttexture_atlas: \"%s\" %d %d\n", depthTabs(depth), ta->name.c_str(), ta->xs, ta->ys);
        }
        else {
            printf("%s\ttexture: \"%s\"\n", depthTabs(depth), model->texture->name.c_str());
        }
    if (model->sequences.size())
    {
        printSequences(model->sequences, depth+1);
    }
    if (model->children.size())
    {
        for (int i = 0; i < model->children.size(); ++i)
            printModel(model->children[i], depth+1);
    }
    printf("%send:\n", depthTabs(depth));
}

void testModel()
{
    std::vector<Anim*>& anims = g_modelData.anims;
    std::vector<Model*>& models = g_modelData.models;
    
    printf("----- verify input -----\n");
    printf("# anim: %d\n# model: %d\n", anims.size(), models.size());
    printf("--------------------------\n");
    printf("//anims\n");
    for (int i = 0 ; i < anims.size(); ++i)
    {
        Anim* anim = anims[i];
        printf("anim: \"%s\"\n", anim->name.c_str());
        printf("\ttype: %s\n", animTypeToString(anim->type));
        printf("\tfrom: ");
        for (int j = 0 ; j < anim->from.size() ; ++j)
            printf("%.2f ", anim->from[j]);
        printf("\n");
        printf("\tto: ");
        for (int j = 0 ; j < anim->to.size() ; ++j)
            printf("%.2f ", anim->to[j]);
        printf("\n");
        printf("\tduration: %.2f\n", anim->duration);
        printf("end:\n");
    }
    
    printf("//models\n");
    for (int i = 0 ; i < models.size(); ++i)
    {
        Model* model = models[i];
        printModel(model, false);
    }
}

// -----------------------------------------------------------------------------
// Parsing 
// -----------------------------------------------------------------------------

bool breakup(NSArray* parsedArray)
{
    NSCharacterSet* charSet = 
        [NSCharacterSet characterSetWithCharactersInString:@":\t"];
    NSArray* keyWords = [NSArray arrayWithObjects:
                         @"model", // 0 
                         @"anim",  // 1
                         @"texture", // 2 
                         @"texture_atlas", // 3 
                         @"child", // 4
                         @"sequence", // 5 
                         @"loop", // 6
                         @"pos", // 7 
                         @"rot",  // 8
                         @"scale",// 9 
                         @"from",// 10 
                         @"to", // 11 
                         @"duration", // 12 
                         @"end", // 13 
                         @"type", // 14 
                         @"geom", // 15
                        nil];
    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:[keyWords count]]; 
    
    // -- convert array in to a dictionary easier that way
    int cnt = 0;
    for (NSString* item in keyWords)
    {
        [dict setValue:[NSNumber numberWithInt:cnt] forKey:item];
        ++cnt;
    }
    
    // --- 
    Model* curModel = 0;
    Anim*  curAnim = 0;
    Sequence* curSequence = NULL;
    NSLog(@"here %@", [parsedArray count]);
    std::vector<Model*> modelStack;
    std::vector<Sequence*> sequenceStack;
    
    for (int i = 0 ; i < [parsedArray count]; ++i)
    {
        NSArray* line = [[parsedArray objectAtIndex:i] 
                         componentsSeparatedByCharactersInSet:charSet];
        
        int next = 0;
              
        // look for key words (model, geom, texture, texuture_atlas

        while (next < [line count])
        {
            NSString* item = [line objectAtIndex:next];

            next++;
			NSLog(@"item>> %@", item);
            NSNumber *number = [dict valueForKey:item];
            if (number != nil)
            {
                int kwrdcnt = [number intValue];
                
                if (kwrdcnt == 0) // model
                {
                    curModel = g_modelData.addModel();
                    modelStack.push_back(curModel);
                    setName(curModel->name, [line objectAtIndex:next]);
                    break;
                }
                else if(kwrdcnt == 1) // anim
                {
                    if (curSequence != NULL)
                    {
                        std::string str = [[line objectAtIndex:next] UTF8String];
                        curSequence->animNames.push_back(str);
                    }
                    else 
                    {
                        if (curAnim != 0)
                        {
                            NSLog(@"Error: - anim %s was not terminated properly! Exiting!", curAnim->name.c_str());
                            return false;
                        }
                        
                        curAnim = g_modelData.addAnim(); 
                        setName(curAnim->name, [line objectAtIndex:next]);
                    }
                    break;
                }
                else if(kwrdcnt == 2)  // texture
                {
                    curModel->texture = new_Texture([line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 3)  // texture_atlas
                {
                    curModel->texture = new_TextureAtlas([line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 4)  // child
                {
                    Model* parent = curModel;
                    curModel = new Model();
                    setName(curModel->name, [line objectAtIndex:next]);
                    modelStack.push_back(curModel);

                    if (parent != 0)
                    {
                        parent->children.push_back(curModel);
                    }
                    else
                    {
                        NSLog(@"Error: child must be defined in a models' scope. Exiting!");
                        return false;
                    }
                    
                    break;
                }
                else if (kwrdcnt == 5) // sequence
                {
                    if (curModel)
                    {
                        if (curSequence == 0)
                        {
                            curModel->sequences.push_back(new Sequence());
                            curSequence = curModel->sequences.back(); 
                            curSequence->loop = 0; // default to indefinite
                            setSequenceParams(*curSequence, [line objectAtIndex:next]);
                            sequenceStack.push_back(curSequence);
                        }
                        else
                        {
                            Sequence* sequence = new Sequence();
                            sequence->loop = 0;
                            setSequenceParams(*sequence, [line objectAtIndex:next]);
                            curSequence->children.push_back(sequence);
                            curSequence = sequence;
                            sequenceStack.push_back(curSequence);
                        }
                    }
                    else 
                    {
                        NSLog(@"error: sequence must be defined in a model's scope\n");
                        return false;
                    }

                    break;
                }
                else if (kwrdcnt == 6) // sequence->loop
                {
                    curSequence->loop = [[line objectAtIndex:next] intValue];
                    break;
                }
                else if (kwrdcnt == 7)  // pos
                {
                    setVec2(curModel->pos, [line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 8) // rot 
                {
                    setFloat(curModel->rot, [line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 9) // scale
                {
                    setVec2(curModel->scale, [line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 10) // from 
                {
                    if (curAnim == 0)
                    {
                        NSLog(@"error: null anim for from. Check spelling of anim key.");
                        return false;
                    }
                    
                    toFloatVector(curAnim->from, [line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 11) // to
                {
                    if (curAnim == 0)
                    {
                        NSLog(@"error: null anim for to. Check spelling of anim key.");
                        return false;
                    }
                    
                    toFloatVector(curAnim->to, [line objectAtIndex:next]);
                    break;
                }
                else if (kwrdcnt == 12) // duration
                {
                    if (curAnim == 0)
                    {
                        NSLog(@"error: null anim for duration. Check spelling of anim key.");
                        return false;
                    }
                    curAnim->duration = [[line objectAtIndex:next] floatValue];
                    break;
                }
                else if (kwrdcnt == 13) // end
                {
                    if (curAnim)
                    {
                        curAnim = 0;
                    }
                    else if (curSequence) 
                    { 
                        sequenceStack.pop_back();
                        if (sequenceStack.size())
                        {
                            curSequence = sequenceStack.back();
                        }
                        else
                        {
                            curSequence = 0;                            
                        }
                    }
                    else if (curModel) 
                    {
                        modelStack.pop_back();
                        if (modelStack.size())
                        {
                            curModel = modelStack.back();
                        }
                        else 
                        {
                            curModel = 0;                            
                        }
                    }
                    break;
                }
                else if (kwrdcnt == 14) // type
                {
                    // if we're in anim 
                    if (curAnim)
                    {
                        toAnimType(curAnim->type, [line objectAtIndex:next]);
                    }
                    else
                    {
                        NSLog(@"Error: invalid anim trying to set the type. Check spelling.");
                        return false;
                    }
                    break;
                }
                else if (kwrdcnt == 15) // geom
                {
                    if (curModel)
                    {
                        toGeomType(curModel->geom.type, [line objectAtIndex:next]);
                    }
                    break;
                }
                else {
                    NSLog(@"warning: unimplemented key %@", item);
                    break;
                }

            }
            else {
                // mention things that were not identified.                

                // skip comments
                if ([item compare:@"//" options:NSCaseInsensitiveSearch range:NSMakeRange(0,2)] == NSOrderedSame)
                {

                    continue;
                }
                else if ([item compare:@""]  != NSOrderedSame)
                {   
                    NSLog(@"unknown key %@", item);
                }
            }
            
        }
    }
    
    return true; // OK
}

void parseModelFile(const char* i_filename)
{
    NSError*  oError;
    NSString* filename = [NSString stringWithUTF8String:i_filename];
    NSString* file = [NSString 
                      stringWithContentsOfFile:filename
                      encoding:NSASCIIStringEncoding 
                      error:&oError];
	NSLog(@"file->%@ / %@", filename, file);
    NSCharacterSet* charSet = 
        [NSCharacterSet characterSetWithCharactersInString:@"\n"];

    NSArray* array = [file componentsSeparatedByCharactersInSet:charSet];
	NSLog(@"-->@", array);
    breakup(array);
    testModel();
}

void testAnims()
{
	// test anims
	Model*  modelRez = g_modelData.findModelRez("name2");
}

int main (int argc, const char * argv[]) 
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


//    if (argc > 1)
    {
//        NSLog(@"Foo %s", argv[1]);
//        const char* filename = "//Users/terryfranguiadakis/Documents/Projects/BioBreak/biobreak/models/model.bbm";
        const char* filename = "//Users/terry/Project/bioBreak/biobreak/models/model_tst.bbm";

        parseModelFile(filename);
    }
    
    [pool drain];
    return 0;
}
